﻿using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using DaveSexton.Labs;

namespace System.Diagnostics
{
	public class TraceSource
	{
		#region Public Properties
		public string Name
		{
			get
			{
				Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));

				return name;
			}
		}

		public IList<TraceListener> Listeners
		{
			get
			{
				Contract.Ensures(Contract.Result<IList<TraceListener>>() != null);

				return listeners;
			}
		}
		#endregion

		#region Private / Protected
		private readonly string name;
		private readonly IList<TraceListener> listeners = new List<TraceListener>();
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="TraceSource" /> class.
		/// </summary>
		public TraceSource(string name)
		{
			Contract.Requires(!string.IsNullOrEmpty(name));

			this.name = name;
		}
		#endregion

		#region Methods
		[ContractInvariantMethod]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Required for code contracts.")]
		private void ObjectInvariant()
		{
			Contract.Invariant(!string.IsNullOrEmpty(name));
			Contract.Invariant(listeners != null);
		}

		public void TraceInformation(string message)
		{
			TraceEvent(TraceEventType.Information, 0, message);
		}

		public void TraceInformation(string format, params object[] args)
		{
			TraceEvent(TraceEventType.Information, 0, format, args);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.TraceSource.TraceEvent(System.Diagnostics.TraceEventType,System.Int32,System.String)",
			Justification = "Same output as the FCL TraceTransfer method.")]
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "relatedActivityId",
			Justification = "Same output as the FCL TraceTransfer method.")]
		public void TraceTransfer(int id, string message, Guid relatedActivityId)
		{
			TraceEvent(TraceEventType.Transfer, id, message + ", relatedActivityId=" + relatedActivityId.ToString());
		}

		public void TraceData(TraceEventType eventType, int id, object data)
		{
			TraceData(eventType, id, new[] { data });
		}

		public void TraceData(TraceEventType eventType, int id, params object[] data)
		{
			var message = data == null
				? string.Empty
				: data.Aggregate(
						new StringBuilder(),
						(builder, o) => builder.Append(o).Append(','),
						builder =>
							builder.Length > 0
							? builder.ToString(0, builder.Length - 1)
							: string.Empty);

			TraceEvent(eventType, id, message);
		}

		public void TraceEvent(TraceEventType eventType, int id)
		{
			TraceEvent(eventType, id, string.Empty);
		}

		public void TraceEvent(TraceEventType eventType, int id, string format, params object[] args)
		{
			TraceEvent(eventType, id, LabTraceSource.Format(format, args));
		}

		public void TraceEvent(TraceEventType eventType, int id, string message)
		{
			message = message ?? string.Empty;

			lock (TraceSourceExtensions.Gate)
			{
				for (int i = 0; i < listeners.Count; i++)
				{
					var listener = listeners[i];

					Contract.Assume(listener != null);

					listener.TraceEvent(null, name, eventType, id, message);
				}
			}
		}
		#endregion
	}
}